package jugadores;

import cartas.Naipe;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
import cartas.mazo;

/**
 *
 * @author robertorosas
 */
public class GameAI extends Thread {

    static int MAXDEPTH = 36;

    public GameAI(mazo unknown, mazo compHand, Naipe vida, mazo inPlay, Maquina currentMachine) {

        this.unknown = unknown;
        this.depth = 0;
        this.vida = vida;
        this.inPlay = new mazo(inPlay);
        this.compHand = compHand;
        this.humanHand = new mazo();
        this.cardsWon = new mazo();
        this.currentMachine = currentMachine;
    }

    public void removeUnknownCard(Naipe card) {
        unknown.sacarNaipe(card);
    }

    public void addInPlay(Naipe card) {
        inPlay.anadirNaipe(card);
    }

    public void addToCompHand(Naipe card) {
        compHand.anadirNaipe(card);
    }

    @Override
    public void run() {
        int d = 2;
        while (true) {
            currentMachine.setChoice(jugarAux(d++));
        }
    }

    public int jugarAux(int maxDepth) {
        double[] v = {0, 0, 0};
        double bestValue = 0;
        int bestMove = 0;
        depth = 0;
        int numUnknown = unknown.getSize();

        System.out.println("numUnknown = " + numUnknown);

        if (inPlay.getSize() == 0) {
            compMovedFirst = true;

            /*            if (numUnknown > 3) { */
            for (int i = 0; i < numUnknown - 2; i++) {
                for (int j = i + 1; j < numUnknown - 1; j++) {
                    for (int k = j + 1; k < numUnknown; k++) {
                        mazo savedUnknown = new mazo(unknown);
                        humanHand.anadirNaipe(unknown.sacarNaipe(k));
                        humanHand.anadirNaipe(unknown.sacarNaipe(j));
                        humanHand.anadirNaipe(unknown.sacarNaipe(i));

                        for (int c = 0; c < compHand.getSize(); c++) {
                            mazo savedCompHand = new mazo(compHand);
                            inPlay.anadirNaipe(compHand.sacarNaipe(c));
                            cardsWon = new mazo();
                            v[c] += expectiMiniMax(-1, 121, maxDepth);
                            compHand = savedCompHand;
                        }
                        bestValue = v[0];
                        bestMove = 0;

                        for (int c = 1; c < compHand.getSize(); c++) {
                            if (v[c] > bestValue) {
                                bestValue = v[c];
                                bestMove = c;
                            }
                        }
                        //currentMachine.setChoice(bestMove);
                        humanHand.sacarNaipe();
                        humanHand.sacarNaipe();
                        humanHand.sacarNaipe();
                        unknown = savedUnknown;
                    }
                }
            }

        /*        } else if (numUnknown > 2) {
        for (int j = 0; j < numUnknown - 1; j++) {
        for (int k = 0; k < numUnknown; k++) {
        mazo savedUnknown = new mazo(unknown);
        humanHand.anadirNaipe(vida);
        humanHand.anadirNaipe(unknown.sacarNaipe(j));
        humanHand.anadirNaipe(unknown.sacarNaipe(k));
        for (int c = 0; c < compHand.getSize(); c++) {
        mazo savedCompHand = new mazo(compHand);
        inPlay.anadirNaipe(compHand.sacarNaipe(c));
        cardsWon = new mazo();
        v[c] += expectiMiniMax(-1, 121, maxDepth);
        compHand = savedCompHand;
        }
        bestValue = v[0];
        bestMove = 0;
        for (int c = 1; c < compHand.getSize(); c++) {
        if (v[c] > bestValue) {
        bestValue = v[c];
        bestMove = c;
        }
        }
        currentMachine.setChoice(bestMove);
        humanHand.sacarNaipe();
        humanHand.sacarNaipe();
        humanHand.sacarNaipe();
        unknown = savedUnknown;
        }
        }
        }
         */

        } else {
            compMovedFirst = false;
            int n1 = compHand.getSize();

            for (int c1 = 0; c1 < n1; c1++) {
                mazo savedCompHand1 = new mazo(compHand);
                inPlay.anadirNaipe(compHand.sacarNaipe(c1));
                cardsWon = new mazo();
                //               inPlay.sacarNaipe();
                boolean compWonRound = wonRound();
                isCompTurn = compWonRound;

                /*
                 * End of Round
                 * 
                 */

                if (compWonRound) {
                    cardsWon.anadirNaipe(inPlay.sacarNaipe());
                    cardsWon.anadirNaipe(inPlay.sacarNaipe());
                } else {
                    inPlay.sacarNaipe();
                    inPlay.sacarNaipe();
                }

                //depth++;

                if (isTerminal(maxDepth)) {
                    v[c1] = value();
                } else {

                    if (numUnknown <= 3) {
                        mazo savedCompHand2 = new mazo(compHand);
                        mazo savedUnknown2 = new mazo(unknown);
                        if (compWonRound) {
                            compHand.anadirNaipe(unknown.sacarNaipe());
                            humanHand.anadirNaipe(vida);
                        } else {
                            humanHand.anadirNaipe(unknown.sacarNaipe());
                            compHand.anadirNaipe(vida);
                        }
                        if (numUnknown >= 2) {
                            humanHand.anadirNaipe(unknown.sacarNaipe());
                        }
                        if (numUnknown >= 3) {
                            humanHand.anadirNaipe(unknown.sacarNaipe());
                        }

                        v[c1] = expectiMiniMax(-1, 121, maxDepth);
                        if (numUnknown >= 3) {
                            humanHand.sacarNaipe();
                        }
                        if (numUnknown >= 2) {
                            humanHand.sacarNaipe();
                        }
                        humanHand.sacarNaipe();
                        unknown = savedUnknown2;
                        compHand = savedCompHand2;
                    } else {
                        for (int c2 = 0; c2 < numUnknown; c2++) {
                            mazo savedUnknown2 = new mazo(unknown);
                            mazo savedCompHand2 = new mazo(compHand);
                            compHand.anadirNaipe(unknown.sacarNaipe(c2));

                            int newNumUnknown = unknown.getSize();
                            for (int i = 0; i < newNumUnknown - 2; i++) {
                                for (int j = i + 1; j < newNumUnknown - 1; j++) {
                                    for (int k = j + 1; k < newNumUnknown; k++) {
                                        mazo savedUnknown = new mazo(unknown);
                                        humanHand.anadirNaipe(unknown.sacarNaipe(k));
                                        humanHand.anadirNaipe(unknown.sacarNaipe(j));
                                        humanHand.anadirNaipe(unknown.sacarNaipe(i));

                                         v[c1] += expectiMiniMax(-1, 121, maxDepth);
                                            //               inPlay.sacarNaipe();
                                        //currentMachine.setChoice(bestMove);
                                        humanHand.sacarNaipe();
                                        humanHand.sacarNaipe();
                                        humanHand.sacarNaipe();
                                        unknown = savedUnknown;
                                    }
                                }
                            }
                            compHand = savedCompHand2;
                            unknown = savedUnknown2;
                        }
                    }

                    //depth--;
                    compHand = savedCompHand1;
                }

            }
        }

        bestValue = v[0];
        bestMove = 0;

        for (int c = 1; c < compHand.getSize(); c++) {
            if (v[c] > bestValue) {
                bestValue = v[c];
                bestMove = c;
            }
        }
        return (bestMove);
    }

    private boolean isTerminal(int maxDepth) {
        return ((compHand.getSize() == 0 && humanHand.getSize() == 0) || depth >= maxDepth);
    }

    private double value() {
        return (cardsWon.contar() + unknown.contar() / 2);
    }

    private double expectiMiniMax(double alpha, double beta, int maxDepth) {


        /*
         * Turno CPU y no End of Round
         * 
         */
        if (inPlay.getSize() < 2 && isCompTurn) {
            double v = -1;

            isCompTurn = false;

            for (int c = 0; c < compHand.getSize(); c++) {
                mazo savedCompHand = new mazo(compHand);
                inPlay.anadirNaipe(compHand.sacarNaipe(c));
                v = Math.max(v, expectiMiniMax(alpha, beta, maxDepth));
                //               inPlay.sacarNaipe();
                compHand = savedCompHand;
                if (v >= beta) {
                    return v;
                }
                if (v > alpha) {
                    alpha = v;
                }
            }

            isCompTurn = true;

            return v;
        }
        /*
         *  Turno Humano y no End of Round
         * 
         */
        if (inPlay.getSize() < 2 && !isCompTurn) {
            double v = 121;

            isCompTurn = true;

            for (int c = 0; c < humanHand.getSize(); c++) {
                mazo savedHumanHand = new mazo(humanHand);
                inPlay.anadirNaipe(humanHand.sacarNaipe(c));
                v = Math.min(v, expectiMiniMax(alpha, beta, maxDepth));
                //             inPlay.sacarNaipe();
                humanHand = savedHumanHand;
                if (v <= alpha) {
                    return v;
                }
                if (v < beta) {
                    beta = alpha;
                }
            }

            isCompTurn = false;

            return v;
        }

        boolean compWonRound = wonRound();
        int numUnknown = unknown.getSize();
        double v = 0;


       isCompTurn = compWonRound;

        /*
         * End of Round
         * 
         */

        if (compWonRound) {
            cardsWon.anadirNaipe(inPlay.sacarNaipe());
            cardsWon.anadirNaipe(inPlay.sacarNaipe());
        } else {
            inPlay.sacarNaipe();
            inPlay.sacarNaipe();
        }


    
          
        if (isTerminal(maxDepth)) {
            return value();
        }

        depth++; 
        
        System.out.println("depth = " + depth);

        if (numUnknown >= 2) {
            for (int j = 0; j < numUnknown; j++) {
                for (int k = 0; k < numUnknown; k++) {
                    if (j != k) {
                        mazo savedUnknown = new mazo(unknown);
                        /*                       if (compWonRound) {
                        if (k < j) {
                        humanHand.anadirNaipe(unknown.sacarNaipe(j));
                        compHand.anadirNaipe(unknown.sacarNaipe(k));
                        } else {
                        humanHand.anadirNaipe(unknown.sacarNaipe(k));
                        compHand.anadirNaipe(unknown.sacarNaipe(j));
                        }
                        } else {
                        if (k < j) {
                        humanHand.anadirNaipe(unknown.sacarNaipe(j));
                        compHand.anadirNaipe(unknown.sacarNaipe(k));
                        } else {
                        humanHand.anadirNaipe(unknown.sacarNaipe(k));
                        compHand.anadirNaipe(unknown.sacarNaipe(j));
                        }
                        }
                         * */

                        if (k < j) {
                            humanHand.anadirNaipe(unknown.sacarNaipe(j));
                            compHand.anadirNaipe(unknown.sacarNaipe(k));
                        } else {
                            humanHand.anadirNaipe(unknown.sacarNaipe(k));
                            compHand.anadirNaipe(unknown.sacarNaipe(j));
                        }

                        v += expectiMiniMax(alpha, beta, maxDepth);
                        humanHand.sacarNaipe();
                        compHand.sacarNaipe();
                        unknown = savedUnknown;
                    }

                }
            }
            depth--;
            return (v / (numUnknown * (numUnknown - 1)));
        }

        if (numUnknown == 1) {  // LAST HAND
            mazo savedUnknown = new mazo(unknown);
            if (compWonRound) {
                compHand.anadirNaipe(unknown.sacarNaipe());
                humanHand.anadirNaipe(vida);
            } else {
                humanHand.anadirNaipe(unknown.sacarNaipe());
                compHand.anadirNaipe(vida);
            }

            v = expectiMiniMax(alpha, beta, maxDepth);
            humanHand.sacarNaipe();
            compHand.sacarNaipe();
            unknown =
                    savedUnknown;
            depth--;
            return v;
        }

        v = expectiMiniMax(alpha, beta, maxDepth);
        depth--;
        return (v);
    }

    private boolean wonRound() {
        Naipe carta1, carta2;
        if (compMovedFirst) {
            //Naipe carta1 = inPlay.getNaipe(compMovedFirst ? 0 : 1);
            //Naipe carta2 = inPlay.getNaipe(compMovedFirst ? 1 : 0);
            carta1 = inPlay.getNaipe(0);
            carta2 = inPlay.getNaipe(1);
        } else {
            carta1 = inPlay.getNaipe(1);
            carta2 = inPlay.getNaipe(0);
        }

        System.out.println(carta1.cara() + " " + carta2.cara());

        if (!carta1.nombreFlor().equals(vida.nombreFlor()) && carta2.nombreFlor().equals(vida.nombreFlor())) {
            return false;
        } else if (!carta1.nombreFlor().equals(carta2.nombreFlor())) {
            return true;
        }

        return carta1.valor() >= carta2.valor();
    }
// private fields 
    private mazo unknown;
    private mazo compHand;
    private mazo humanHand;
    private mazo inPlay;
    private mazo cardsWon;
    private Boolean isCompTurn;
    private Boolean compMovedFirst;
    private int depth;
    private Naipe vida;
    private Maquina currentMachine;
}
